package com.zhijian.medical.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import com.google.common.collect.Lists;
import com.zhijian.medical.dao.local.ExperimentProcessSampleMapper;
import com.zhijian.medical.entity.po.*;
import com.zhijian.medical.entity.vo.request.ExperimentProcessSampleSearchVo;
import com.zhijian.medical.entity.vo.response.ExperimentProcessSampleVo;
import com.zhijian.medical.entity.vo.response.ExperimentTypeEnumVo;
import com.zhijian.medical.enums.*;
import com.zhijian.medical.holder.CurrentUserHolder;
import com.zhijian.medical.service.*;
import com.zhijian.medical.util.AssertUtil;
import com.zhijian.medical.util.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 实验流程-样本管理业务处理层
 *
 * @author HCG
 * @version: 1.0
 * @date 2022-04-13 23:32:04
 */
@Slf4j
@Service
public class ExperimentProcessSampleServiceImpl implements ExperimentProcessSampleService {

    @Autowired
    private ExperimentProcessSampleMapper mapper;

    @Autowired
    private ExperimentProcessSampleLogService experimentProcessSampleLogService;

    @Autowired
    private PcrService pcrService;

    @Autowired
    private SangerService sangerService;

    @Autowired
    private NgsService ngsService;

    @Autowired
    private FishService fishService;

    @Override
    public ExperimentProcessSample selectById(Long id) {
        return mapper.selectByPrimaryKey(id);
    }

    @Override
    public ExperimentProcessSample selectByFzblh(String fzblh) {
        return mapper.selectByFzblh(fzblh);
    }

    @Override
    public ExperimentProcessSampleVo selectVoById(Long id) {
        return mapper.selectVoById(id);
    }

    @Transactional(rollbackFor = Exception.class, value = "localTransactionManager")
    @Override
    public void deleteById(Long id) {
        experimentProcessSampleLogService.delete(id);
        mapper.deleteByPrimaryKey(id);
    }

    @Transactional(rollbackFor = Exception.class, value = "localTransactionManager")
    @Override
    public Long insert(ExperimentProcessSample experimentProcessSample) {
        experimentProcessSample.setMoleculePathologyNumber(this.createNextMoleculePathologyNumber());
        //是否合格
        Byte weatherQualified = experimentProcessSample.getWeatherQualified();
        AssertUtil.check(weatherQualified == ExperimentProcessSampleWeatherQualifiedEnum.BHG.getCode().byteValue() && StringUtils.isBlank(experimentProcessSample.getUnqualifiedReason()), "不合格原因不能为空");
        Date now = new Date();
        experimentProcessSample.setCreateTime(now);
        experimentProcessSample.setUpdateTime(now);
        experimentProcessSample.setReceiveDate(now);
        //状态默认待检测，如果不合格就直接不合格
        experimentProcessSample.setStatus(weatherQualified == ExperimentProcessSampleWeatherQualifiedEnum.HG.getCode().byteValue() ? (byte) 0 : (byte) 99);
        List<String> projectList = Arrays.asList(experimentProcessSample.getProject().split(","));
        boolean isCov = projectList.contains(ExperimentTypeEnum.PCR.getProject().get(7));
        AssertUtil.check(isCov && projectList.size() > 1, "新冠病毒不能与其他项目一起选择");
        AssertUtil.check(isCov && !experimentProcessSample.getType().equals(ExperimentProcessSampleTypeEnum.YSZ.getDesc()), "新冠病毒的样本类型必须是咽拭子");
        AssertUtil.check(!isCov && experimentProcessSample.getType().equals(ExperimentProcessSampleTypeEnum.YSZ.getDesc()), "样本类型为咽拭子时，检测项目必须为新冠病毒");
        long experimentProcessSampleId = mapper.insert(experimentProcessSample) > 0 ? experimentProcessSample.getId() : 0L;
        experimentProcessSampleLogService.create(ExperimentProcessSampleLog.builder()
                .sampleId(experimentProcessSampleId)
                .mainType(experimentProcessSample.getMainType())
                .op(weatherQualified == ExperimentProcessSampleWeatherQualifiedEnum.HG.getCode().byteValue() ? "创建" : String.format("%s，%s", ExperimentProcessSampleWeatherQualifiedEnum.BHG.getDesc(), experimentProcessSample.getUnqualifiedReason()))
                .opUser(CurrentUserHolder.getUser().getName())
                .build()
        );
        return experimentProcessSampleId;
    }

    @Transactional(rollbackFor = Exception.class, value = "localTransactionManager")
    @Override
    public boolean insertAll(List<ExperimentProcessSample> experimentProcessSampleList) {
        experimentProcessSampleList.forEach(this::insert);
        return true;
    }

    @Override
    public boolean update(ExperimentProcessSample experimentProcessSample) {
        ExperimentProcessSample oldExperimentProcessSample = this.selectById(experimentProcessSample.getId());
        AssertUtil.notNull(oldExperimentProcessSample, "此样本不存在或者已被删除");
        List<String> projectList = Arrays.asList(experimentProcessSample.getProject().split(","));
        boolean isCov = projectList.contains(ExperimentTypeEnum.PCR.getProject().get(7));
        AssertUtil.check(isCov && projectList.size() > 1, "新冠病毒不能与其他项目一起选择");
        AssertUtil.check(isCov && !experimentProcessSample.getType().equals(ExperimentProcessSampleTypeEnum.YSZ.getDesc()), "新冠病毒的样本类型必须是咽拭子");
        AssertUtil.check(!isCov && experimentProcessSample.getType().equals(ExperimentProcessSampleTypeEnum.YSZ.getDesc()), "样本类型为咽拭子时，检测项目必须为新冠病毒");
        Date now = new Date();
        experimentProcessSample.setUpdateTime(now);
        experimentProcessSample.setReceiveDate(oldExperimentProcessSample.getReceiveDate());
        experimentProcessSampleLogService.create(ExperimentProcessSampleLog.builder()
                .sampleId(experimentProcessSample.getId())
                .op("修改")
                .opUser(CurrentUserHolder.getUser().getName())
                .build()
        );
        return mapper.updateByPrimaryKey(experimentProcessSample) != 0;
    }

    @Override
    public boolean updateByPrimaryKeySelective(ExperimentProcessSample record) {
        Date now = new Date();
        record.setUpdateTime(now);
        return mapper.updateByPrimaryKeySelective(record) != 0;
    }

    @Override
    public boolean removeRelation(Long experimentProcessId, Byte mainType) {
        return mapper.removeRelation(experimentProcessId, mainType);
    }

    @Override
    public Integer count(ExperimentProcessSampleSearchVo experimentProcessSampleSearchVo) {
        return mapper.count(experimentProcessSampleSearchVo);
    }

    @Override
    public List<ExperimentProcessSampleVo> fishSampleList() {
        List<ExperimentProcessSampleVo> experimentProcessSampleVos = selectListByStatusAndMainType();
        ArrayList<ExperimentProcessSampleVo> sampleList = new ArrayList<>();

        experimentProcessSampleVos.forEach(s -> {
            if (s.getType().equals(ExperimentProcessSampleTypeEnum.LK.getDesc()) && s.getStatus() == ExperimentFishSampleStatusEnum.NEW.getCode().byteValue()) {
                return;
            }
            sampleList.add(s);
        });
        return sampleList;
    }

    private List<ExperimentProcessSampleVo> selectListByStatusAndMainType() {
        return mapper.selectListByStatusAndMainType("0,1", String.valueOf(ExperimentTypeEnum.FISH.getCode()));
    }

    @Override
    public List<ExperimentProcessSampleVo> selectList(ExperimentProcessSampleSearchVo experimentProcessSampleSearchVo) {
        return mapper.selectList(experimentProcessSampleSearchVo);
    }

    @Override
    public Page<ExperimentProcessSampleVo> selectPage(ExperimentProcessSampleSearchVo experimentProcessSampleSearchVo) {
        //先直接查总数
        int count = count(experimentProcessSampleSearchVo);
        //分页
        Page<ExperimentProcessSampleVo> page = new Page<>(true, count, experimentProcessSampleSearchVo.getPageNum(), experimentProcessSampleSearchVo.getPageSize());
        experimentProcessSampleSearchVo.setStartIndex(page.getStartIndex());
        page.setPage(true);
        page.setTotalCount(count);
        List<ExperimentProcessSampleVo> responseVos = this.selectList(experimentProcessSampleSearchVo);
        page.setData(responseVos);
        return page;
    }

    @Override
    public boolean updateNoteById(Long id, String note) {
        return updateByPrimaryKeySelective(ExperimentProcessSample.builder().id(id).note(note).build());
    }

    @Override
    public String selectMaxMoleculePathologyNumberByYear(String year) {
        return mapper.selectMaxMoleculePathologyNumberByYear(year);
    }

    @Override
    public String createNextMoleculePathologyNumber() {
        //今年
        String year = DateUtil.format(new Date(), new SimpleDateFormat("yyyy"));
        //今年最大的分子病理号
        String maxMoleculePathologyNumberByYear = this.selectMaxMoleculePathologyNumberByYear(year);
        if (StringUtils.isBlank(maxMoleculePathologyNumberByYear)) {
            //如果为空，说明这是今年第一个
            return String.format("M%s%s", year, "00001");
        }
        //替换掉M后，剩下的加1即可
        String m = maxMoleculePathologyNumberByYear.replace("M", "");
        Integer mI = Integer.valueOf(m);
        return String.format("M%s", mI + 1);
    }

    @Override
    public ExperimentProcessSampleVo selectVoByMoleculePathologyNumber(String moleculePathologyNumber) {
        List<ExperimentProcessSampleVo> experimentProcessSampleVos = this.selectList(ExperimentProcessSampleSearchVo.builder().moleculePathologyNumber(moleculePathologyNumber).build());
        return CollectionUtil.isNotEmpty(experimentProcessSampleVos) ? experimentProcessSampleVos.get(0) : null;
    }

    @Override
    public List<ExperimentTypeEnumVo> selectExperimentTypeEnumVo() {
        ExperimentTypeEnum[] experimentTypeEnums = ExperimentTypeEnum.values();
        return Arrays.stream(experimentTypeEnums).map(e -> {
            ExperimentTypeEnumVo experimentTypeEnumVo = new ExperimentTypeEnumVo();
            experimentTypeEnumVo.setCode(e.getCode());
            experimentTypeEnumVo.setName(e.getDesc());
            experimentTypeEnumVo.setProject(e.getProject());
            return experimentTypeEnumVo;
        }).collect(Collectors.toList());
    }

    @Override
    public List<ExperimentProcessSampleVo> selectWaitReady(Integer drType) {
        List<ExperimentProcessSampleVo> result = Lists.newLinkedList();
        if (drType == RNADNAEnum.DNA.getCode().intValue()) {
            //dna
            //荧光PCR
            result.addAll(this.selectList(ExperimentProcessSampleSearchVo.builder()
                    .mainType(ExperimentTypeEnum.PCR.getCode().byteValue())
                    .status(PcrSampleStatusEnum.NEW.getCode().byteValue())
                    //查询的项目，除去新冠病毒
                    .projectList(ExperimentTypeEnum.PCR.getProject().subList(0, 7))
                    .weatherQualified(ExperimentProcessSampleWeatherQualifiedEnum.HG.getCode().byteValue())
                    .build()));
            //一代测序，现在的都是DNA，所以直接不用查项目了
            result.addAll(this.selectList(ExperimentProcessSampleSearchVo.builder()
                    .mainType(ExperimentTypeEnum.SANGER.getCode().byteValue())
                    .status(SangerSampleStatusEnum.NEW.getCode().byteValue())
                    .weatherQualified(ExperimentProcessSampleWeatherQualifiedEnum.HG.getCode().byteValue())
                    .build()));
            //二代测序，现在的都是DNA，所以直接不用查项目了
            result.addAll(this.selectList(ExperimentProcessSampleSearchVo.builder()
                    .mainType(ExperimentTypeEnum.NGS.getCode().byteValue())
                    .status(NgsSampleStatusEnum.NEW.getCode().byteValue())
                    .weatherQualified(ExperimentProcessSampleWeatherQualifiedEnum.HG.getCode().byteValue())
                    .build()));
        } else if (drType == RNADNAEnum.FISH.getCode().intValue()) {
            //fish
            result.addAll(this.selectList(ExperimentProcessSampleSearchVo.builder()
                    .mainType(ExperimentTypeEnum.FISH.getCode().byteValue())
                    .status(ExperimentFishSampleStatusEnum.NEW.getCode().byteValue())
                    .type(ExperimentProcessSampleTypeEnum.LK.getDesc())
                    .weatherQualified(ExperimentProcessSampleWeatherQualifiedEnum.HG.getCode().byteValue())
                    .build()));
        } else {
            //rna暂时没有 todo
        }
        return result;
    }

    @Override
    public List<ExperimentProcessSampleVo> selectWaitHstqByType(Integer drType) {
        List<ExperimentProcessSampleVo> result = Lists.newLinkedList();
        if (drType == RNADNAEnum.DNA.getCode().intValue()) {
            //dna
            //荧光PCR
            result.addAll(this.selectList(ExperimentProcessSampleSearchVo.builder()
                    .mainType(ExperimentTypeEnum.PCR.getCode().byteValue())
                    .status(PcrSampleStatusEnum.YZB.getCode().byteValue())
                    //查询的项目，除去新冠病毒
                    .projectList(ExperimentTypeEnum.PCR.getProject().subList(0, 7))
                    .weatherQualified(ExperimentProcessSampleWeatherQualifiedEnum.HG.getCode().byteValue())
                    .build()));
            //一代测序，现在的都是DNA，所以直接不用查项目了
            result.addAll(this.selectList(ExperimentProcessSampleSearchVo.builder()
                    .mainType(ExperimentTypeEnum.SANGER.getCode().byteValue())
                    .status(SangerSampleStatusEnum.YZB.getCode().byteValue())
                    .weatherQualified(ExperimentProcessSampleWeatherQualifiedEnum.HG.getCode().byteValue())
                    .build()));
            //二代测序，现在的都是DNA，所以直接不用查项目了
            result.addAll(this.selectList(ExperimentProcessSampleSearchVo.builder()
                    .mainType(ExperimentTypeEnum.NGS.getCode().byteValue())
                    .status(NgsSampleStatusEnum.YZB.getCode().byteValue())
                    .weatherQualified(ExperimentProcessSampleWeatherQualifiedEnum.HG.getCode().byteValue())
                    .build()));
        } else if (drType == RNADNAEnum.FISH.getCode().intValue()) {
            //fish 不需要核酸提取
        } else if (drType == RNADNAEnum.COV.getCode().intValue()) {
            //新冠病毒
            result.addAll(this.selectList(ExperimentProcessSampleSearchVo.builder()
                    .mainType(ExperimentTypeEnum.PCR.getCode().byteValue())
                    .status(PcrSampleStatusEnum.NEW.getCode().byteValue())
                    //项目为新冠病毒
                    .project(ExperimentTypeEnum.PCR.getProject().get(7))
                    .weatherQualified(ExperimentProcessSampleWeatherQualifiedEnum.HG.getCode().byteValue())
                    .build()));
        } else {
            //rna暂时没有 todo
        }
        return result;

    }

    @Transactional(rollbackFor = Exception.class, value = "localTransactionManager")
    @Override
    public boolean cancelInProcess(Long id, String cancelReason, Byte handleType) {
        ExperimentProcessSample experimentProcessSample = this.selectById(id);
        AssertUtil.notNull(experimentProcessSample, "此样本不存在或者已被删除");
        Long oldExperimentProcessId = experimentProcessSample.getExperimentProcessId();
        experimentProcessSample.setStatus(handleType == 0 ? (byte) 0 : (byte) 98);
        experimentProcessSample.setNote(cancelReason);
        //脱离实验
        experimentProcessSample.setExperimentProcessId(null);
        this.update(experimentProcessSample);
        Byte mainType = experimentProcessSample.getMainType();
        ExperimentTypeEnum experimentTypeEnum = ExperimentTypeEnum.getByCode(mainType.intValue());
        assert experimentTypeEnum != null;
        String name = "";
        String processNumber = "";
        switch (experimentTypeEnum) {
            case PCR:
                Pcr pcr = pcrService.selectById(oldExperimentProcessId);
                if (Objects.nonNull(pcr)) {
                    name = pcr.getName();
                    processNumber = pcr.getProcessNumber();
                }
                break;
            case SANGER:
                Sanger sanger = sangerService.selectById(oldExperimentProcessId);
                if (Objects.nonNull(sanger)) {
                    name = sanger.getName();
                    processNumber = sanger.getProcessNumber();
                }
                break;
            case NGS:
                Ngs ngs = ngsService.selectById(oldExperimentProcessId);
                if (Objects.nonNull(ngs)) {
                    name = ngs.getName();
                    processNumber = ngs.getProcessNumber();
                }
                break;
            case FISH:
                ExperimentFish experimentFish = fishService.selectByPrimaryKey(oldExperimentProcessId);
                if (Objects.nonNull(experimentFish)) {
                    name = experimentFish.getName();
                    processNumber = experimentFish.getExperimentNum();
                }
                break;
            default:
                break;
        }
        String e = String.format("%s%s", name, processNumber);
        //处理方式，0：重新实验，1：退费
        experimentProcessSampleLogService.create(ExperimentProcessSampleLog.builder()
                .sampleId(id)
                .mainType(experimentProcessSample.getMainType())
                .op(handleType == 0 ? String.format("%s-%s，原因：%s", e, "重新实验", cancelReason) : String.format("%s-%s，原因：%s", e, "退费", cancelReason))
                .opUser(CurrentUserHolder.getUser().getName())
                .build()
        );
        return true;
    }

    @Transactional(rollbackFor = Exception.class, value = "localTransactionManager")
    @Override
    public boolean createNewFromBhg(ExperimentProcessSample experimentProcessSample, Pcr pcr) {
        //modify at 2022-09-16，改成如果需要重新实验的样本，生成一个新的样本，只不过样本病理号不一样，然后状态就是已核酸提取完毕，然后记录各自的日志
        ExperimentProcessSample newExperimentProcessSample = new ExperimentProcessSample();
        BeanUtils.copyProperties(experimentProcessSample, newExperimentProcessSample);
        newExperimentProcessSample.setFromId(experimentProcessSample.getId());
        newExperimentProcessSample.setMoleculePathologyNumber(this.createNextMoleculePathologyNumber());
        newExperimentProcessSample.setStatus(PcrSampleStatusEnum.YTQ.getCode().byteValue());
        //实验为null
        newExperimentProcessSample.setExperimentProcessId(null);
        //新样本的id
        Long newId = mapper.insert(newExperimentProcessSample) > 0 ? newExperimentProcessSample.getId() : 0L;

        //如果样本存在，而且正确存在于这个实验当中，就修改状态到 不合格
        this.updateByPrimaryKeySelective(ExperimentProcessSample.builder()
                .id(experimentProcessSample.getId())
                .status(PcrSampleStatusEnum.BHG.getCode().byteValue())
                .build());

        experimentProcessSampleLogService.create(ExperimentProcessSampleLog.builder()
                .sampleId(experimentProcessSample.getId())
                .mainType(experimentProcessSample.getMainType())
                .op(String.format("不合格，原因：%s", String.format("%s%s，核酸扩增一区，阴性室内质控污染或者阳性室内质控不在控。生成新的样本进行重新实验，分子病理号为%s", pcr.getName(), pcr.getProcessNumber(), newExperimentProcessSample.getMoleculePathologyNumber())))
                .opUser(CurrentUserHolder.getUser().getName())
                .build());

        experimentProcessSampleLogService.create(ExperimentProcessSampleLog.builder()
                .sampleId(newId)
                .mainType(experimentProcessSample.getMainType())
                .op(String.format("创建，来源：%s-%s", pcr.getName(), pcr.getProcessNumber()))
                .opUser(CurrentUserHolder.getUser().getName())
                .build());
        return true;
    }

}